/****************************************************************************
 *              fresnel.c -
 *  Calculation of Fresnel integrals by expansion to Chebyshev series
 *  Expansions are taken from the book
 *  Y.L. Luke. Mathematical functions and their approximations.
 *  Moscow, "Mir", 1980. PP. 145-149 (Russian edition)
 ****************************************************************************
 */
/*
  Modified for Ruby bindings
          2006/Dec/24 Y. TSUNESADA
*/
#include <math.h>
#include "include/rb_gsl.h"
#include "include/rb_gsl_sf.h"

static const double sqrt_pi_2   = 1.2533141373155002512078826424; /* sqrt(pi/2) */
static const double sqrt_2_pi   = 0.7978845608028653558798921199; /* sqrt(2/pi) */
static const double _1_sqrt_2pi = 0.3989422804014326779399460599; /* 1/sqrt(2*pi) */
static const double pi_2        = 1.5707963267948966192313216916; /* pi/2 */

static double f_data_a[18] =
{
  0.76435138664186000189,
  -0.43135547547660179313,
  0.43288199979726653054,
  -0.26973310338387111029,
  0.08416045320876935378,
  -0.01546524484461381958,
  0.00187855423439822018,
  -0.00016264977618887547,
  0.00001057397656383260,
  -0.00000053609339889243,
  0.00000002181658454933,
  -0.00000000072901621186,
  0.00000000002037332546,
  -0.00000000000048344033,
  0.00000000000000986533,
  -0.00000000000000017502,
  0.00000000000000000272,
  -0.00000000000000000004
};

static double f_data_b[17] =
{
  0.63041404314570539241,
  -0.42344511405705333544,
  0.37617172643343656625,
  -0.16249489154509567415,
  0.03822255778633008694,
  -0.00564563477132190899,
  0.00057454951976897367,
  -0.00004287071532102004,
  0.00000245120749923299,
  -0.00000011098841840868,
  0.00000000408249731696,
  -0.00000000012449830219,
  0.00000000000320048425,
  -0.00000000000007032416,
  0.00000000000000133638,
  -0.00000000000000002219,
  0.00000000000000000032
};

static double fresnel_cos_0_8(double x)
{
  double x_8 = x/8.0;
  double xx = 2.0*x_8*x_8 - 1.0;

  double t0 = 1.0;
  double t1 = xx;
  double sumC = f_data_a[0] + f_data_a[1]*t1;
  double t2;
  int n;
  for (n = 2; n < 18; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    sumC += f_data_a[n]*t2;
    t0 = t1; t1 = t2;
  }
  return _1_sqrt_2pi*sqrt(x)*sumC;
}

static double fresnel_sin_0_8(double x)
{
  double x_8 = x/8.0;
  double xx = 2.0*x_8*x_8 - 1.0;
  double t0 = 1.;
  double t1 = xx;
  double ot1 = x_8;
  double ot2 = 2.0*x_8*t1 - ot1;
  double sumS = f_data_b[0]*ot1 + f_data_b[1]*ot2;
  int n;
  double t2;
  for (n = 2; n < 17; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    ot1 = ot2;
    ot2 = 2.0*x_8*t2 - ot1;
    sumS += f_data_b[n]*ot2;
    t0 = t1; t1 = t2;
  }
  return _1_sqrt_2pi*sqrt(x)*sumS;
}

static double f_data_e[41] =
{
  0.97462779093296822410,
  -0.02424701873969321371,
  0.00103400906842977317,
  -0.00008052450246908016,
  0.00000905962481966582,
  -0.00000131016996757743,
  0.00000022770820391497,
  -0.00000004558623552026,
  0.00000001021567537083,
  -0.00000000251114508133,
  0.00000000066704761275,
  -0.00000000018931512852,
  0.00000000005689898935,
  -0.00000000001798219359,
  0.00000000000594162963,
  -0.00000000000204285065,
  0.00000000000072797580,
  -0.00000000000026797428,
  0.00000000000010160694,
  -0.00000000000003958559,
  0.00000000000001581262,
  -0.00000000000000646411,
  0.00000000000000269981,
  -0.00000000000000115038,
  0.00000000000000049942,
  -0.00000000000000022064,
  0.00000000000000009910,
  -0.00000000000000004520,
  0.00000000000000002092,
  -0.00000000000000000982,
  0.00000000000000000467,
  -0.00000000000000000225,
  0.00000000000000000110,
  -0.00000000000000000054,
  0.00000000000000000027,
  -0.00000000000000000014,
  0.00000000000000000007,
  -0.00000000000000000004,
  0.00000000000000000002,
  -0.00000000000000000001,
  0.00000000000000000001
};

static double f_data_f[35] =
{
  0.99461545179407928910,
  -0.00524276766084297210,
  0.00013325864229883909,
  -0.00000770856452642713,
  0.00000070848077032045,
  -0.00000008812517411602,
  0.00000001359784717148,
  -0.00000000246858295747,
  0.00000000050925789921,
  -0.00000000011653400634,
  0.00000000002906578309,
  -0.00000000000779847361,
  0.00000000000222802542,
  -0.00000000000067239338,
  0.00000000000021296411,
  -0.00000000000007041482,
  0.00000000000002419805,
  -0.00000000000000861080,
  0.00000000000000316287,
  -0.00000000000000119596,
  0.00000000000000046444,
  -0.00000000000000018485,
  0.00000000000000007527,
  -0.00000000000000003131,
  0.00000000000000001328,
  -0.00000000000000000574,
  0.00000000000000000252,
  -0.00000000000000000113,
  0.00000000000000000051,
  -0.00000000000000000024,
  0.00000000000000000011,
  -0.00000000000000000005,
  0.00000000000000000002,
  -0.00000000000000000001,
  0.00000000000000000001
};

static double fresnel_cos_8_inf(double x)
{
  double xx = 128.0/(x*x) - 1.0;  /* 2.0*(8/x)^2 - 1 */
  double t0 = 1.0;
  double t1 = xx;
  double sumP = f_data_e[0] + f_data_e[1]*t1;
  double sumQ = f_data_f[0] + f_data_f[1]*t1;
  double t2;
  int n;
  for(n = 2; n < 35; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    sumP += f_data_e[n]*t2; /*  sumP += f_data_e[n]*ChebyshevT(n,xx) */
    sumQ += f_data_f[n]*t2; /*  sumQ += f_data_f[n]*ChebyshevT(n,xx) */
    t0 = t1; t1 = t2;
  }
  for(n = 35; n < 41; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    sumP += f_data_e[n]*t2; /*  sumP += f_data_e[n]*ChebyshevT(n,xx) */
    t0 = t1; t1 = t2;
  }
  return 0.5 - _1_sqrt_2pi*(0.5*sumP*cos(x)/x - sumQ*sin(x))/sqrt(x);
}

static double fresnel_sin_8_inf(double x)
{
  double xx = 128.0/(x*x) - 1.0;  /* 2.0*(8/x)^2 - 1 */
  double t0 = 1.0;
  double t1 = xx;
  double sumP = f_data_e[0] + f_data_e[1]*t1;
  double sumQ = f_data_f[0] + f_data_f[1]*t1;
  double t2;
  int n;
  for(n = 2; n < 35; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    sumP += f_data_e[n]*t2; /*  sumP += f_data_e[n]*ChebyshevT(n,xx) */
    sumQ += f_data_f[n]*t2; /*  sumQ += f_data_f[n]*ChebyshevT(n,xx) */
    t0 = t1; t1 = t2;
  }
  for(n = 35; n < 41; n++)
  {
    t2 = 2.0*xx*t1 - t0;
    sumP += f_data_e[n]*t2; /*  sumQ += f_data_f[n]*ChebyshevT(n,xx) */
    t0 = t1; t1 = t2;
  }
  return 0.5 - _1_sqrt_2pi*(0.5*sumP*sin(x)/x + sumQ*cos(x))/sqrt(x);
}


double fresnel_c(double x)
{
  double xx = x*x*pi_2;
  double ret_val;
  if(xx<=8.0)
    ret_val = fresnel_cos_0_8(xx);
  else
    ret_val = fresnel_cos_8_inf(xx);
  return (x<0.0) ? -ret_val : ret_val;
}

double fresnel_s(double x)
{
  double xx = x*x*pi_2;
  double ret_val;
  if(xx<=8.0)
    ret_val = fresnel_sin_0_8(xx);
  else
    ret_val = fresnel_sin_8_inf(xx);
  return (x<0.0) ? -ret_val : ret_val;
}

double fresnel_c1(double x)
{
  return fresnel_c(x*sqrt_2_pi);
}

double fresnel_s1(double x)
{
  return fresnel_s(x*sqrt_2_pi);
}

static VALUE rb_fresnel_c(VALUE obj, VALUE x)
{
  return rb_gsl_sf_eval1(fresnel_c, x);
}
static VALUE rb_fresnel_s(VALUE obj, VALUE x)
{
  return rb_gsl_sf_eval1(fresnel_s, x);
}
static VALUE rb_fresnel_c1(VALUE obj, VALUE x)
{
  return rb_gsl_sf_eval1(fresnel_c1, x);
}
static VALUE rb_fresnel_s1(VALUE obj, VALUE x)
{
  return rb_gsl_sf_eval1(fresnel_s1, x);
}
void Init_fresnel(VALUE module)
{
  VALUE mfresnel;
  mfresnel = rb_define_module_under(module, "Fresnel");
  rb_define_module_function(module, "fresnel_c", rb_fresnel_c, 1);
  rb_define_module_function(module, "fresnel_s", rb_fresnel_s, 1);
  rb_define_module_function(module, "fresnel_c1", rb_fresnel_c1, 1);
  rb_define_module_function(module, "fresnel_s1", rb_fresnel_s1, 1);
  rb_define_module_function(mfresnel, "c", rb_fresnel_c, 1);
  rb_define_module_function(mfresnel, "s", rb_fresnel_s, 1);
  rb_define_module_function(mfresnel, "c1", rb_fresnel_c1, 1);
  rb_define_module_function(mfresnel, "s1", rb_fresnel_s1, 1);
}











